using System;
using System.IO;
using FileArchive;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;

namespace Zip
{
	public class ZipArchive : IFileArchive
	{
		#region IFileArchive Members
        /// <summary>
        /// Creates the archive.
        /// </summary>
        /// <param name="ArchiveFilePath">The archive file path.</param>
        /// <param name="SolutionFilePath">The solution file path.</param>
        /// <param name="FilePaths">The file paths.</param>
        /// <param name="RelativePath">if set to <c>true</c> [relative path].</param>
		public void CreateArchive(string ArchiveFilePath, 
			string SolutionFilePath, string[] FilePaths, bool RelativePath)
		{
			this.CreateZipFile(ArchiveFilePath, SolutionFilePath,
				FilePaths, RelativePath);
		}
		#endregion
		static private string ZipEntry(string FilePath, 
			bool IncludeFileName)
		{
			string path = "";
			DirectoryInfo di = new DirectoryInfo(FilePath);
			if(di.Root.FullName == di.Root.Name)
			{
				path = (di.Parent == null) ? 
					"" : di.Parent.FullName.Replace(di.Root.FullName, "");
			}
			else
			{
				path = di.Parent.FullName.Substring(
					di.Parent.FullName.IndexOf(di.Root.Name));
			}
			if( ( ! path.EndsWith(@"\")) &&
				(path.Length > 0) ) 
			{
				path += @"\";
			}
			if(IncludeFileName)
			{
				path += di.Name;
			}
			return path;
		}
		static private string RootZipEntry(string FilePath, string FullZipEntry,
			ref string RootZipPath, ref string FullRootPath)
		{
			string zipEntry = "";
			if(FullZipEntry == null)
			{
				FullZipEntry = ZipArchive.ZipEntry(FilePath, true);
			}
			if( (FilePath != null) && (FilePath != "") )
			{
				zipEntry = ZipArchive.ZipEntry(FilePath, true);
				if( (zipEntry != FullZipEntry) &&
					( ! zipEntry.EndsWith(@"\")) )
				{
					zipEntry += @"\";
				}
				DirectoryInfo di = new DirectoryInfo(FilePath);
				if( (RootZipPath.IndexOf(zipEntry) == -1) &&
					(di.Parent != null) )
				{
					FullRootPath = di.Parent.FullName;
					zipEntry = ZipArchive.RootZipEntry(FullRootPath, 
						FullZipEntry, ref RootZipPath, ref FullRootPath);
				}
				else
				{
					RootZipPath = zipEntry;
					zipEntry = FullZipEntry.Replace(zipEntry, "");
				}
			}
			return zipEntry;
		}
		static private string RootPathFromFiles(string InitRootPath,
			string[] FilePaths)
		{
			string path = "";
			DirectoryInfo di = new DirectoryInfo(InitRootPath);
			if(di.Exists)
			{
				path = ZipArchive.ZipEntry(InitRootPath, true);
			}
			else
			{
				FileInfo fi = new FileInfo(InitRootPath);
				if(fi.Exists)
				{
					path = ZipArchive.ZipEntry(InitRootPath, false);
				}
			}
			foreach(string filePath in FilePaths)
			{
				FileInfo fileInfo = new FileInfo(filePath);
				if(fileInfo.Exists)
				{
					string initPath = ZipArchive.ZipEntry(
						InitRootPath, false);
					string zipRootPath = initPath;
					string fullRootPath = InitRootPath;
					string zipPath = ZipArchive.RootZipEntry(
						filePath, null, ref zipRootPath, ref fullRootPath);
					if( (initPath.IndexOf(zipRootPath) == 0) &&
						(zipRootPath.Length < initPath.Length) )
					{
						return ZipArchive.RootPathFromFiles(fullRootPath,
							FilePaths);
					}
				}
			}
			return path;
		}
		private void CreateZipFile(string ArchiveFilePath, 
			string SolutionFilePath, string[] FilePaths, bool RelativePath)
		{
			const int blockSize = 16384;
			FileStream fs = new FileStream(ArchiveFilePath, FileMode.Create);
			ZipOutputStream zipStream = new ZipOutputStream(fs);
			string zipRootPath = ZipArchive.RootPathFromFiles(
				SolutionFilePath, FilePaths);
			foreach(string filePath in FilePaths)
			{
				FileInfo fileInfo = new FileInfo(filePath);
				if(fileInfo.Exists)
				{
					FileStream streamToZip = new FileStream(filePath, 
						FileMode.Open, FileAccess.Read);
					string zipPath = "";
					if(RelativePath)
					{
						string fullRootPath = filePath;
						zipPath = ZipArchive.RootZipEntry(filePath, 
							null, ref zipRootPath, ref fullRootPath);
					}
					else
					{
						zipPath = ZipArchive.ZipEntry(filePath, true);
					}
					ZipEntry zipEntry = new ZipEntry(zipPath);
					zipStream.PutNextEntry(zipEntry);
					byte[] buffer = new byte[blockSize];
					int size = streamToZip.Read(buffer, 0, buffer.Length);
					zipStream.Write(buffer, 0, size);
					while(size < streamToZip.Length) 
					{
						int sizeRead = streamToZip.Read(buffer,0,buffer.Length);
						zipStream.Write(buffer, 0, sizeRead);
						size += sizeRead;
					}
					streamToZip.Close();
				}
			}
			zipStream.Finish();
			zipStream.Close();
			fs.Close();
		}
		public ZipArchive() {}
	}
}
